﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Reflection;
using System.ComponentModel;

namespace KgUtils.Wpf.AttachedProperties
{
    /// <summary>
    /// Permet de gérer les permissions en binding.
    /// </summary>
    /// <example>
    /// IsEnabled :
    /// <Button Content="Save" tools:Permission.IsEnabled="Admin,Editor" Command={Binding SaveClickCommand} />
    /// 
    /// IsVisibility :
    /// <Button Content="Save" tools:Permission.Visibility="Admin" Command={Binding SaveClickCommand} />
    /// </example>
    /// <remarks>
    /// Voir http://stackoverflow.com/questions/7173854/how-to-control-roles-permissions-in-xaml-with-enumerators/7175097#7175097
    /// </remarks>
    public class Permission
    {
        #region private methods

        private static void RecalculateControlVisibility(UIElement control, bool hasPermission)
        {
            //Si la propriété est bindée, définir une valeur manuellement va casser le binding
            //Donc si hasPermission est à faux, on doit casser tout binding existant, mais pas si l'utilisateur a l'accès.
            if (hasPermission == false)
                control.Visibility = Visibility.Collapsed;
        }

        private static void RecalculateControlIsEnabled(Control control, bool hasPermission)
        {
            //Si la propriété est bindée, définir une valeur manuellement va casser le binding
            //Donc si hasPermission est à faux, on doit casser tout binding existant, mais pas si l'utilisateur a l'accès.
            if (hasPermission == false)
                control.IsEnabled = hasPermission;
        }

        #endregion

        #region Visibility

        public static readonly DependencyProperty VisibilityProperty =
            DependencyProperty.RegisterAttached("Visibility", typeof(string), typeof(Permission), new PropertyMetadata(Visibility_Callback));

        private static void Visibility_Callback(DependencyObject source, DependencyPropertyChangedEventArgs e)
        {
            bool hasPermission = false;

            var uiElement = (UIElement)source;
            var permissions = GetVisibility(uiElement).Split(',');

            foreach (var item in permissions)
            {
                hasPermission = HasUserPermission(uiElement, item.Trim()); //check if this permission is in users permission list
                if (hasPermission)
                    break;
            }

            RecalculateControlVisibility(uiElement, hasPermission);

            if (!hasPermission)
            {
                //Lors du premier passage, il semblerait que le binding ne soit pas encore mis en place.
                //Donc définir la valeur ne supprime pas le binding. 
                //Donc dans le doute on intercepte l'événement de changement et on reforce la donnée à false.
                uiElement.IsVisibleChanged += new DependencyPropertyChangedEventHandler(uiElement_IsVisibleChanged);
            }
        }

        static void uiElement_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            RecalculateControlVisibility(sender as Control, false);
        }

        public static void SetVisibility(UIElement element, string value)
        {
            element.SetValue(VisibilityProperty, value);
        }

        public static string GetVisibility(UIElement element)
        {
            return (string)element.GetValue(VisibilityProperty);
        }

        #endregion

        #region IsEnabled

        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.RegisterAttached("IsEnabled", typeof(string), typeof(Permission), new PropertyMetadata(IsEnabled_Callback));

        private static void IsEnabled_Callback(DependencyObject source, DependencyPropertyChangedEventArgs e)
        {
            bool hasPermission = false;

            var uiElement = (Control)source;
            var permissions = GetIsEnabled(uiElement).Split(',');

            foreach (var item in permissions)
            {
                hasPermission = HasUserPermission(uiElement, item.Trim()); //check if this permission is in users permission list
                if (hasPermission)
                    break;
            }

            RecalculateControlIsEnabled(uiElement, hasPermission);

            if (!hasPermission)
            {
                //Lors du premier passage, il semblerait que le binding ne soit pas encore mis en place.
                //Donc définir la valeur ne supprime pas le binding. 
                //Donc dans le doute on intercepte l'événement de changement et on reforce la donnée à false.
                uiElement.IsEnabledChanged += new DependencyPropertyChangedEventHandler(uiElement_IsEnabledChanged);
            }
        }

        static void uiElement_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            RecalculateControlIsEnabled(sender as Control, false);
        }

        public static void SetIsEnabled(UIElement element, string value)
        {
            element.SetValue(IsEnabledProperty, value);
        }

        public static string GetIsEnabled(UIElement element)
        {
            return (string)element.GetValue(IsEnabledProperty);
        }

        #endregion

        private static bool HasUserPermission(UIElement element, string pCodeDroit)
        {
            //en mode design, on considère que l'utilisateur a la permission sinon les controle n'apparaissent pas dans le designer !
            if (!DesignerProperties.GetIsInDesignMode(element))
                return false; //INSERT HERE A BUSINESS RULE TO CHECK PERMISSION
            else
                return true;
        }
    }
}
